
package com.turing.pre.processor.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itextpdf.text.DocumentException;
import com.turing.common.annotations.RLockAnnotation;
import com.turing.common.config.ServerConfig;
import com.turing.common.response.ResponseCode;
import com.turing.common.response.ResponseVO;
import com.turing.common.util.*;
import com.turing.pre.processor.dao.PreProcessorTaskMapper;
import com.turing.pre.processor.domain.FileImage;
import com.turing.pre.processor.domain.PreProcessorTask;
import com.turing.pre.processor.enums.AnalysisStatusEnum;
import com.turing.pre.processor.enums.FileTypeEnum;
import com.turing.pre.processor.enums.PreProcessorStatusEnum;
import com.turing.pre.processor.file.processor.File2PdfInLinux;
import com.turing.pre.processor.file.processor.FileToPdf;
import com.turing.pre.processor.file.processor.PDFProcessor;
import com.turing.pre.processor.file.processor.TextParser;
import com.turing.pre.processor.inner.FileTaskInnerService;
import com.turing.pre.processor.service.FileImageService;
import com.turing.pre.processor.service.PreProcessorTaskService;
import com.turing.pre.processor.service.TaskThread;
import com.turing.pre.processor.vo.in.FileImagesVo;
import com.turing.pre.processor.vo.in.FileTaskVo;
import com.turing.pre.processor.vo.in.ImageVo;
import net.coobird.thumbnailator.Thumbnails;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.concurrent.Future;
import java.util.stream.Collectors;


/**
 * @Description 服务类实现
 * @Author lcy
 * @Date 2021-10-28
 */
@Service
public class PreProcessorTaskServiceImpl extends ServiceImpl<PreProcessorTaskMapper, PreProcessorTask> implements PreProcessorTaskService {

    @Value( "${fdfs.domain}" )
    private String FDFS_FILE_DOMAIN;

    private final Logger logger = LoggerFactory.getLogger(PreProcessorTaskServiceImpl.class);

    @Autowired
    private PreProcessorTaskMapper preProcessorTaskMapper;

    @Autowired
    private ServerConfig serverConfig;

    @Autowired
    private PreProcessorTaskService preProcessorTaskService;

    @Autowired
    private PDFProcessor pdfProcessor;

    @Autowired
    private FileImageService fileImageService;

    @Autowired
    private FileTaskInnerService fileTaskInnerService;

    @Value( "${main.path}" )
    private String mainPath;

    private final String sep = "/";

    @Autowired
    private TaskThread service;

    @Override
    public PreProcessorTask getTaskByMainStatus(int status, String domain, List<String> fileTypes) {

        return new LambdaQueryChainWrapper<>(preProcessorTaskMapper)
                .eq(PreProcessorTask::getPreProcessorStatus, status)
                .eq(PreProcessorTask::getDomain, domain)
                .eq(PreProcessorTask::getIsDel, 0)
                .in(PreProcessorTask::getFileType, fileTypes)
                .orderByDesc(PreProcessorTask::getCreatedTime)
                .last("limit 1")
                .one();

    }

    @Override
    public boolean updateStatusById(int id, int status) {
        return new LambdaUpdateChainWrapper<>(preProcessorTaskMapper)
                .eq(PreProcessorTask::getId, id)
                .set(PreProcessorTask::getPreProcessorStatus, status).update();
    }

    @Override
    public void buildTaskStatus(PreProcessorTask task, String fileType, int elec) {

        List<String> docTypes = Arrays.asList(FileTypeEnum.DOC.getName(), FileTypeEnum.DOCX.getName() ,FileTypeEnum.TXT.getName());
        List<String> photoTypes = Arrays.asList(FileTypeEnum.TIF.getName(), FileTypeEnum.TIFF.getName(), FileTypeEnum.JPEG.getName()
                , FileTypeEnum.PNG.getName(), FileTypeEnum.BMP.getName());

        // default
        task.setAnalysisStatus(AnalysisStatusEnum.INACTIVE.getCode());

        if (docTypes.contains(fileType)) {
            task.setPreProcessorStatus(PreProcessorStatusEnum.WAITINGFILE2PDF.getCode());
        } else if (fileType.equals(FileTypeEnum.PDF.getName())) {
            task.setPreProcessorStatus(PreProcessorStatusEnum.WAITINGPDF2JPG.getCode());
            if (elec == 1) {
                task.setAnalysisStatus(AnalysisStatusEnum.WAITINGSPLITING.getCode());
            }
        } else if(photoTypes.contains(fileType)){
            task.setPreProcessorStatus(PreProcessorStatusEnum.WAITINGPHOTO2JPG.getCode());
        }

    }

    @Override
    public PreProcessorTask getTaskByAnalysisStatus(int status, String url) {
        return new LambdaQueryChainWrapper<>(preProcessorTaskMapper)
                .eq(PreProcessorTask::getAnalysisStatus, status)
                .eq(PreProcessorTask::getIsDel, 0)
                .orderByDesc(PreProcessorTask::getCreatedTime)
                .last("limit 1")
                .one();
    }

    @Override
    @Transactional
    public void savePreTask(FileTaskVo fileTaskVo) {
        try{
            String rootPath = mainPath + "/" + UUID.randomUUID() + "/";
            String savePath = rootPath + fileTaskVo.getFileName() + "." + fileTaskVo.getFileType();
            File newFile = new File(savePath);

            File file = FileUtils.downloadFile(FDFS_FILE_DOMAIN + fileTaskVo.getFileUrl());
            FileUtils.copyFile(file, newFile);

            PreProcessorTask task = PreProcessorTask.builder().parentTaskSn(fileTaskVo.getParentTaskSn())
                    .taskSn(fileTaskVo.getTaskSn())
                    .domain(serverConfig.getUrl())
                    .fileUrl(fileTaskVo.getFileUrl())
                    .originalPath(rootPath + fileTaskVo.getFileName())
                    .rootPath(rootPath)
                    .fileName(fileTaskVo.getFileName())
                    .fileType(fileTaskVo.getFileType())
                    .belongsElec(fileTaskVo.getBelongsElec())
                    .build();
            if (fileTaskVo.getFileType().equals(FileTypeEnum.PDF.getName())) {
                task.setPdfPath(savePath);
            }
            buildTaskStatus(task, fileTaskVo.getFileType(), fileTaskVo.getBelongsElec());
            save(task);
        }catch (Exception e){
            fileTaskInnerService.preprocessorFailedCallback(fileTaskVo.getTaskSn());
        }


    }

    public void doWord2pdf(PreProcessorTask preProcessorTask) {
        String originalPath = preProcessorTask.getOriginalPath();
        int index = originalPath.lastIndexOf("/");
        String pdfPath = originalPath + ".pdf";
        String osName = System.getProperty("os.name");
        boolean success = false;

        try {
            if (osName.contains("Windows")) {
                success = FileToPdf.fileToPdf(preProcessorTask.getOriginalPath()+"." + preProcessorTask.getFileType(), originalPath + ".pdf", preProcessorTask.getFileType());
            }else{
                success = File2PdfInLinux.word2pdfInLinux(preProcessorTask.getOriginalPath() + "." + preProcessorTask.getFileType() ,
                        preProcessorTask.getOriginalPath().substring(0,index));
                Thread.sleep(4000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        logger.info("start trans word to pdf...rsult is:"+success);
        if (success) {
            lambdaUpdate().eq(PreProcessorTask::getId,preProcessorTask.getId())
                    .set(PreProcessorTask::getPreProcessorStatus, PreProcessorStatusEnum.WAITINGPDF2JPG.getCode())
                    .set(PreProcessorTask::getAnalysisStatus, AnalysisStatusEnum.WAITINGSPLITING.getCode())
                    .set(PreProcessorTask::getBelongsElec,1)
                    .set(PreProcessorTask::getPdfPath,pdfPath)
                    .update();
            String pdfUrl = FdfsUtils.uploadFile(new File(pdfPath));
            fileTaskInnerService.savePdfUrl(pdfUrl,preProcessorTask.getTaskSn());
        }else{
            doFailedProcess(preProcessorTask);
        }
    }

    @Override
    public void doTxt2pdf(PreProcessorTask preProcessorTask) {

        String originalPath = preProcessorTask.getOriginalPath();
        String pdfPath = originalPath + ".pdf";
        TextParser parser = TextParser.getInstance();
        try {
            //txt转为pdf
            parser.text2pdf(preProcessorTask.getOriginalPath() + "." + preProcessorTask.getFileType(), pdfPath);
            lambdaUpdate().eq(PreProcessorTask::getId,preProcessorTask.getId())
                    .set(PreProcessorTask::getPreProcessorStatus, PreProcessorStatusEnum.WAITINGPDF2JPG.getCode())
                    .set(PreProcessorTask::getAnalysisStatus, AnalysisStatusEnum.WAITINGSPLITING.getCode())
                    .set(PreProcessorTask::getBelongsElec,1)
                    .set(PreProcessorTask::getPdfPath,pdfPath)
                    .update();
            String pdfUrl = FdfsUtils.uploadFile(new File(pdfPath));
            fileTaskInnerService.savePdfUrl(pdfUrl,preProcessorTask.getTaskSn());
        } catch (DocumentException | IOException e) {
            doFailedProcess(preProcessorTask);
            e.printStackTrace();
        }

    }

    @Override
    public void doPdf2jpg(PreProcessorTask preProcessorTask) {

        String imageRootPath = preProcessorTask.getRootPath() + "/" + "pdf_img_save";
        boolean success = false;
        try {
            success = pdfProcessor.pdf2png(preProcessorTask.getTaskSn(), preProcessorTask.getId(), preProcessorTask.getPdfPath(),
                    preProcessorTask.getFileName(), FileTypeEnum.JPG.getName(), imageRootPath, preProcessorTask.getBelongsElec());
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (success) {

            int preTaskDetailId = preProcessorTask.getId();
            String taskSn = preProcessorTask.getTaskSn();
            List<com.turing.pre.processor.domain.FileImage> fileImageList = fileImageService.list(new LambdaQueryWrapper<FileImage>().eq(FileImage::getTaskSn, taskSn));

            FileImagesVo fileImagesVo = FileImagesVo.builder()
                .taskSn(taskSn)
                .imageVos(fileImageList.stream().map((fileImage) -> ImageVo.builder().pageNo(fileImage.getPageNo()).width(fileImage.getWidth()).height(fileImage.getHeight())
                .imageUrl(fileImage.getImageUrl()).build()).collect(Collectors.toList())).build();

            ResponseVO<String> callbackResponse = null;
            try{
                callbackResponse = fileTaskInnerService.fileImageCallBack(fileImagesVo);
                if (callbackResponse.getCode().equals(ResponseCode.SUCCESS)) {

                    lambdaUpdate().eq(PreProcessorTask::getId,preTaskDetailId)
                            .set(PreProcessorTask::getPreProcessorStatus,PreProcessorStatusEnum.PDF2JPGFINISHED.getCode())
                            .update();

                    // sweep file image records
                    fileImageService.remove(new LambdaQueryWrapper<FileImage>().eq(FileImage::getTaskSn,taskSn));
                    File dir = new File(preProcessorTask.getRootPath());
                    logger.info("safely sweep folders:"+FileUtils.deleteDir(dir));

                }
            }catch (Exception e){
                doFailedProcess(preProcessorTask);
                e.printStackTrace();
            }
        }else{
            doFailedProcess(preProcessorTask);
        }
    }

    @Override
    public void doPdfSplit(PreProcessorTask preProcessorTask) {

        String rootPath = preProcessorTask.getRootPath();
        String pdfPath = preProcessorTask.getPdfPath();
        String fileName = preProcessorTask.getFileName();
        String pdfSplitDir = rootPath + sep + fileName + sep;
        File pdfSplitPackage = new File(pdfSplitDir);
        if (!pdfSplitPackage.exists()) {
            pdfSplitPackage.mkdirs();
        }
        try {
            PDFProcessor.splitPdf(pdfPath, 1, pdfSplitDir, fileName);
        } catch (IOException | DocumentException e) {
            doFailedProcess(preProcessorTask);
            return;
        }

        String textDir = rootPath + sep + fileName + "_text" + sep;

        String tableDir = rootPath + sep + fileName + "_table" + sep;

        File textPackage = new File(textDir);
        if (!textPackage.exists()) {
            textPackage.mkdirs();
        }

        File tablePackage = new File(tableDir);
        if (!tablePackage.exists()) {
            tablePackage.mkdirs();
        }

        preProcessorTask.setPdfSplitDir(pdfSplitDir);
        preProcessorTask.setTextDir(textDir);
        preProcessorTask.setTableDir(tableDir);
        preProcessorTask.setAnalysisStatus(AnalysisStatusEnum.WAITINGELECTEXTANALYSING.getCode());
        preProcessorTaskService.updateById(preProcessorTask);

    }

    @Override
    public void doElecAnalysis(PreProcessorTask preProcessorTask) {

        String pdfPackages = preProcessorTask.getPdfSplitDir();
        //1.获取文件将下所有文件
        List<String> files = FileUtils.getFiles(pdfPackages);
        String tablePackages = preProcessorTask.getTableDir();
        String textPackages = preProcessorTask.getTextDir();
        String fileName = preProcessorTask.getFileName();
        long l = System.currentTimeMillis();

        try {
            List<Future<String>> lstFuture = new ArrayList<>();// 存放所有的线程，用于获取结果
            if (!CollectionUtils.isEmpty(files)) {
                //preProcessorTask.setTotalPage(files.size());
                for (String filePath : files) {
                    Future<String> stringFuture = service.excuteValueTask(filePath, textPackages, tablePackages, fileName);
                    lstFuture.add(stringFuture);

                }
                if (lstFuture.size() == files.size()) {
                    preProcessorTask.setAnalysisStatus(AnalysisStatusEnum.WAITINGJSONASSEMBLING.getCode());
                    preProcessorTaskService.updateById(preProcessorTask);
                } else {
                    doFailedProcess(preProcessorTask);
                }

            }
            long ll = System.currentTimeMillis();
            System.out.println("提取完成处理过程时间  " + (ll - l));
        } catch (Exception e) {
            doFailedProcess(preProcessorTask);
        }

    }

    @Override
    public void doJsonAssembling(PreProcessorTask preProcessorTask) {

        String textPackages = preProcessorTask.getTextDir();

        String rootPath = preProcessorTask.getRootPath();

        String fileName = preProcessorTask.getFileName();

        long l = System.currentTimeMillis();
        //组装 json
        // 文本 +表格
        try {

            // 分页文件列表
            List<String> texts = FileUtils.getFiles(textPackages);

            //JSONArray
            JSONArray jsonArray = new JSONArray();

            String docID = PDFProcessor.getRandomString(12);
            List<String> list = PDFProcessor.sortList(texts);
            for (int i = 0; i < list.size(); i++) {

                String textP = list.get(i);
                JSONArray text = FileUtils.getFilesByJson(textP);
                String tableP = textP.replace(fileName + "_text", fileName + "_table");
                //当前页是否包含表格
                JSONArray table = FileUtils.getFilesByJson(tableP);
                //创建json
                JSONObject jsonObjectPage = new JSONObject();
                //  "pageNo": 0,
                jsonObjectPage.put("pageNo", i);
                // "docID": "v1_page-7-8_IKUZRAXJ011a",

                jsonObjectPage.put("docID", "v1_page-" + i + "-" + list.size() + "_" + docID);

                JSONArray lines = new JSONArray();
                for (int i1 = 0; i1 < text.size(); i1++) {
                    // 逐页处理
                    JSONObject thisPageTextObj = text.getJSONObject(i1);
                    JSONArray textData = thisPageTextObj.getJSONArray("data");

                    if (CollectionUtils.isEmpty(table)) {
                        PDFProcessor.extractedNoTables(i, lines, textData);
                    } else {
                        PDFProcessor.extractedTables(i, table, lines, textData);
                    }

                }
                //坐标排序
                lines.sort(new Comparator<Object>() {
                    @Override
                    public int compare(Object o1, Object o2) {
                        JSONObject jsonObject1 = JSON.parseObject(o1.toString());
                        JSONObject jsonObject2 = JSON.parseObject(o2.toString());
                        Integer lineNo1 = jsonObject1.getInteger("lineNo");
                        Integer lineNo2 = jsonObject2.getInteger("lineNo");
                        return lineNo1 - lineNo2;

                    }
                });
                //lineNo 按顺序 排队
                for (int i1 = 0; i1 < lines.size(); i1++) {
                    JSONObject jsonObject = lines.getJSONObject(i1);
                    jsonObject.put("lineNo", i1);
                    jsonObject.put("sortNo", i1);
                    SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker();
                    jsonObject.put("rowNo", snowflakeIdWorker.nextId());
                    jsonObject.put("objType_postpreprocess", "text_postpreprocess");
                }

                jsonObjectPage.put("lineList", lines);
                jsonArray.add(jsonObjectPage);

            }

            String JSONPath = rootPath + sep + fileName + ".json";
            FileUtils.StringToFile(JSON.toJSONString(jsonArray), JSONPath);
            preProcessorTask.setJsonPath(JSONPath);

            String JsonUrl = FdfsUtils.uploadFile(new File(JSONPath));
            preProcessorTask.setAnalysisStatus(AnalysisStatusEnum.ELEC2JSONFINISHED.getCode());
            preProcessorTask.setJsonUrl(JsonUrl);
            preProcessorTaskService.updateById(preProcessorTask);
            long l2 = System.currentTimeMillis();
            logger.info("飞机装配完成  耗时 :" + (l2 - l));

            // call back
            fileTaskInnerService.analysisCallback( preProcessorTask.getParentTaskSn(),preProcessorTask.getTaskSn(),  JsonUrl );

        } catch (Exception e) {
            doFailedProcess(preProcessorTask);
        }

    }

    @Override
    public int countPendingTasks() {
        return lambdaQuery().notIn(PreProcessorTask::getPreProcessorStatus, PreProcessorStatusEnum.PDF2JPGFINISHED.getCode(), PreProcessorStatusEnum.ERROR.getCode())
                .notIn(PreProcessorTask::getAnalysisStatus, AnalysisStatusEnum.ELEC2JSONFINISHED.getCode(), AnalysisStatusEnum.ERROR.getCode())
                .eq(PreProcessorTask::getIsDel, 0)
                .count();
    }

    @RLockAnnotation(lockKey = "getAndLockPreProcessTask")
    @Override
    public ResponseVO<FileTaskVo> getAndLockPreProcessTask(String domain) {

        return fileTaskInnerService.getAndLockPreProcessTask(domain);

    }

    @Override
    public void doFailedProcess(PreProcessorTask preProcessorTask) {

        lambdaUpdate().eq(PreProcessorTask::getId,preProcessorTask.getId())
                .set(PreProcessorTask::getAnalysisStatus,AnalysisStatusEnum.ERROR.getCode())
                .set(PreProcessorTask::getPreProcessorStatus,PreProcessorStatusEnum.ERROR.getCode())
                .update();

        fileTaskInnerService.preprocessorFailedCallback(preProcessorTask.getTaskSn());

    }

    @Override
    public void doPhoto2jpg(PreProcessorTask preProcessorTask) {

        String filePath = preProcessorTask.getOriginalPath() + "." + preProcessorTask.getFileType();
        File input = new File(filePath);
        File output = new File( preProcessorTask.getOriginalPath() + ".jpg" );
        FileImage fileImage = null;
        try {
            fileImage = normalPhoto2jpg(input, output);
        } catch (IOException e) {
            e.printStackTrace();
        }

        int preTaskDetailId = preProcessorTask.getId();
        String taskSn = preProcessorTask.getTaskSn();
        String url = FdfsUtils.uploadFile(output);

        if(fileImage.equals(new FileImage())){
            doFailedProcess(preProcessorTask);
            return;
        }

        FileImagesVo fileImagesVo = FileImagesVo.builder()
                .taskSn(taskSn)
                .imageVos( Collections.singletonList(ImageVo.builder().pageNo(0).width(fileImage.getWidth()).height(fileImage.getHeight())
                        .imageUrl(url).build()) ).build();

        ResponseVO<String> callbackResponse = null;
        try{
            callbackResponse = fileTaskInnerService.fileImageCallBack(fileImagesVo);
            if (callbackResponse.getCode().equals(ResponseCode.SUCCESS)) {

                lambdaUpdate().eq(PreProcessorTask::getId,preTaskDetailId)
                        .set(PreProcessorTask::getPreProcessorStatus,PreProcessorStatusEnum.PHOTO2JPGFINISHED.getCode())
                        .update();

                // sweep file image records
                File dir = new File(preProcessorTask.getRootPath());
                logger.info("safely sweep folders:"+FileUtils.deleteDir(dir));
                // TODO: 2022/11/22 存在问题，上面去修改了这里又删除了 v1.1版本删除
                removeById(preProcessorTask.getId());
            }
        }catch (Exception e){
            doFailedProcess(preProcessorTask);
            e.printStackTrace();
        }



    }

    private static FileImage normalPhoto2jpg(File input, File outPut) throws IOException {
        //normalPhoto2jpg("D:\\project\\5936515a9c8a3347","D:\\project\\5936515a9c8a3347.png");
        FileImage fileImage = new FileImage();
        BufferedImage bufferedImage;
        try {
            // read image file
            bufferedImage = ImageIO.read(input);
            // create a blank, RGB, same width and height, and a white
            // background
            BufferedImage newBufferedImage = new BufferedImage(
                    bufferedImage.getWidth(), bufferedImage.getHeight(),
                    BufferedImage.TYPE_INT_RGB);
            // TYPE_INT_RGB:创建⼀个RBG图像，24位深度，成功将32位图转化成24位
            newBufferedImage.createGraphics().drawImage(bufferedImage, 0, 0,
                    Color.WHITE, null);
            // write to jpeg file
            fileImage.setWidth(bufferedImage.getWidth());
            fileImage.setHeight(bufferedImage.getHeight());
            //ImageIO.write(newBufferedImage, "jpg", outPut);
            //Img.from(newBufferedImage).write(outPut);
            Thumbnails.of(newBufferedImage).scale(1f).toFile(outPut);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return fileImage;
    }

    public static void main(String[] args) throws IOException {

        normalPhoto2jpg(new File("D:\\project\\34bd213f0fc6e8c52d9861ae2ccf2e90.jpeg"),new File("D:\\project\\a7614b9e43e400c21af61009e967ebc.jpg"));

    }


}
